package com.colud.client.handler;

import com.colud.client.util.*;
import com.colud.client.vo.Message;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayOutputStream;

/**
 * 报文编码
 */
public class Encoder extends SimpleChannelHandler {

    private Logger logger = LoggerFactory.getLogger(getClass());

    @Override
    public void writeRequested(ChannelHandlerContext ctx, MessageEvent e)
            throws Exception {
        ChannelBuffer buffer = buildMessage((Message) e.getMessage());
        if (buffer != null) {
            Channels.write(ctx, e.getFuture(), buffer);
        }
    }

    private ChannelBuffer buildMessage(Message msg) {
        int bodyLength = msg.getMsgBody().capacity();
        ChannelBuffer buffer = ChannelBuffers.buffer(bodyLength + Message.MSG_FIX_LENGTH);
        //--------------数据头----------
        /**
         * 数据长度(包括头标识、数据头、数据体和尾标识) 4字节
         */
        buffer.writeInt(buffer.capacity() + 2);
        /**
         * 报文序列号 4字节
         */
        buffer.writeInt(msg.getMsgSn());
        /**
         * 业务数据类型 2字节
         */
        buffer.writeShort(msg.getMsgId());
        /**
         * 下级平台接入码，上级平台给下级平台分配唯一标识码。4字节
         */
        buffer.writeInt(Constants.DOWN_ACCESS_CODE);
        /**
         * 协议版本号标识，上下级平台之间采用的标准协议版
         * 编号；长度为 3 个字节来表示，0x01 0x02 0x0F 标识
         * 的版本号是 v1.2.15，以此类推。
         * Hex编码 ,这个是3个字节，需要注意
         */
        buffer.writeBytes(msg.getVersionFlag());
        /**
         * 报文加密标识位 b: 0 表示报文不加密，1 表示报文加密。0x00 0x01
         */
        buffer.writeByte(0);
        /**
         * 数据加密的密匙，长度为 4 个字节
         */
        buffer.writeInt(20000000);

        //--------------数据体----------
        buffer.writeBytes(msg.getMsgBody());

        /**
         * 校验数据头和数据体 22 + bodyLength
         *  Head flag + Message Header + CRC Code + End Flag
         *    1 + 22 + 2 + 1 = 26
         */
        byte[] b = ChannelBuffers.buffer(bodyLength + 22).array();
        buffer.getBytes(1, b);
        int crcValue = CRC16.do_crc(b);
        System.out.println("crcValue = " + crcValue);
        /**
         * CRC 校验码 2字节
         */
        buffer.writeShort(crcValue);

        byte[] bytes = buffer.array();
        byte[] formatBytes = null;
        try {
            formatBytes = doEscape4Receive(bytes, 0, bytes.length);
        } catch (Exception e) {
            logger.info(e.getMessage());
        }
        ChannelBuffer finalBuffer = ChannelBuffers.dynamicBuffer(formatBytes.length + 2);
        /**
         * 头标识 1字节
         */
        finalBuffer.writeByte(Message.MSG_HEAD);

        finalBuffer.writeBytes(formatBytes);
        /**
         * 尾标识 1字节
         */
        finalBuffer.writeByte(Message.MSG_TAIL);
        String sendData = ClientUtils.byte2HexString(finalBuffer.array());
        logger.info("sendData ------ " + sendData);
        return finalBuffer;
    }

    /**
     * 报文转义
     * void
     *
     * @param bs
     * @param
     */
    public byte[] doEscape4Receive(byte[] bs, int start, int end) throws Exception {
        if (start < 0 || end > bs.length) {
            throw new ArrayIndexOutOfBoundsException("doEscape4Receive error : index out of bounds(start=" + start
                    + ",end=" + end + ",bytes length=" + bs.length + ")");
        }
        ByteArrayOutputStream baos = null;
        try {
            baos = new ByteArrayOutputStream();
            for (int i = start; i < end; i++) {
                if (bs[i] == 0x5B) {
                    baos.write(0x5A);
                    baos.write(0x01);
                } else if (bs[i] == 0x5A) {
                    baos.write(0x5A);
                    baos.write(0x02);
                } else if (bs[i] == 0x5D) {
                    baos.write(0x5E);
                    baos.write(0x01);
                } else if (bs[i] == 0x5E) {
                    baos.write(0x5E);
                    baos.write(0x02);
                } else {
                    baos.write(bs[i]);
                }
            }
            return baos.toByteArray();
        } catch (Exception e) {
            throw e;
        } finally {
            if (baos != null) {
                baos.close();
                baos = null;
            }
        }
    }
}
